#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include "tunlent.h"
#include "tcp.h"
#include <sys/socket.h>

struct global {
    uint8_t buf[1024 * 1024];
} *global;


// ----------------------------------------------------------------------------------------------

int on_session_open(tcp_sess_t *s)
{
    printf("[s+] %p\n", s);
    tunlent_t *self = (tunlent_t *)malloc(sizeof(tunlent_t));
    if (self == NULL) {
        printf("malloc failed\n");
        return -1;
    }

    memset(self, 0, sizeof(tunlent_t));
    self->sock = s->sock;
    s->user.ptr = self;

    return 0;
}

int on_session_exit(tcp_sess_t *s)
{
    printf("[s-] %p\n", s);
    tunlent_t *self = (tunlent_t *)s->user.ptr;
    if (self->pair != NULL) {
        shutdown(self->pair->sock, SHUT_WR);
    }

    free(self);
    s->user.ptr = NULL;
    return 0;
}

int on_session_recv(tcp_sess_t *s)
{
    uint8_t *buf = global->buf;
    tunlent_t *self = (tunlent_t *)s->user.ptr; // session pack

    int len = recvbuf(s->sock, global->buf, sizeof(global->buf));
    if (len <= 0) {
        if (self->pair == NULL) {
            del_tunlent(self);
        }
        return -1;
    }

    if (!self->tunl.is_got_req) {
        int ret;
        ret = tunl_get_req((char *)buf, len, &self->tunl);
        if (ret < 0) {
            printf("tunnel request failed (%d)\n", ret);
            printf("recv: [%s]\n", global->buf);
            return -1;
        }

        tunlent_t *pair = find_pair_tunlent(self);
        if (pair == NULL) {
            printf("pending\n");

            self->pair = NULL;
            add_tunlent(self);
            return 0;
        } else {
            printf("paired\n");

            self->pair = pair;
            pair->pair = self;
            del_tunlent(pair);
            len = tunl_set_res((char *)buf, &self->tunl);
            int sock = (self->tunl.active) ? self->sock : pair->sock;
            if (sendall(sock, buf, len) <= 0) {
                goto err;
            }
            return 0;
        }
    }

    if (self->pair != NULL) {
        len = sendall(self->pair->sock, buf, len);
        if (len <= 0) {
            goto err;
        }
    } else {
        goto err;
    }

    return 0;
err:
    return -1;
}


int on_ready(void *ptr)
{
    tcp_t *srv = (tcp_t *)ptr;
    printf("server ready. port: %d\n", srv->port);
    return 0;
}

int main(int argc, char *argv[])
{
    global = (struct global *)malloc(sizeof(struct global));
    memset(global, 0, sizeof(struct global));
    init_tunlent();

    tcp_t srv = {
        .port = 9000,
        .on_sock = on_ready,
        .on_open = on_session_open,
        .on_recv = on_session_recv,
        .on_exit = on_session_exit,
    };

    int ret = tcp_new_srv(&srv);
    printf("server exit (%d).\n", ret);

    return 0;
}
